﻿using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using LFSRecord2.Events;
using LFSRecord2.Model;
using LFSRecord2.Model.UserActions;
using LFSRecord2.View.Converters;
using LFSRecord2.View.TimelineStructures;
using System.Windows.Media.Media3D;
using System.Diagnostics;

namespace LFSRecord2.View
{
    /// <summary>
    /// Interaction logic for TimelineLayerPropertyView.xaml
    /// </summary>
    public partial class TimelineLayerPropertyView : UserControl
    {
        DoubleValueEditControl[] _doubleEditControl;
        StringValueEditControl[] _stringEditControl;
        RDIdentValueEditControl[] _replayPlayerEditControl;

        public TimelineLayerPropertyView()
        {
            InitializeComponent();
        }

        private void TimelineLayerPropertyViewControl_Loaded(object sender, RoutedEventArgs e)
        {
            // Create value edit controls
            CompLayerPropertyBase prop = (CompLayerPropertyBase)DataContext;

            // Should we hide the replayname Binding control?
            if (prop.ParentLayer.LayerType == CompLayerTypes.ReplayControl && prop.PropertyName == "Replay" ||
                prop.ParentLayer.LayerType == CompLayerTypes.ReplayData && 
                (prop.PropertyName == "Position" || prop.PropertyName == "Heading"))
            {
                PropertyBindingControl.Visibility = Visibility.Hidden;
            }

            // Prepare for valuedit control
            string appendChar = "";
            switch (prop.PropertyValueMeaning)
            {
                case LayerPropertyValueMeaning.Degrees:
                    appendChar = "°";
                    break;
                case LayerPropertyValueMeaning.Percent:
                    appendChar = "%";
                    break;
                case LayerPropertyValueMeaning.Metres:
                    appendChar = "m";
                    break;
            }

            // create appropriate value edit control(s)
            switch (prop.PropertyValueType)
            {
                case LayerPropertyValueTypes.Point3D:
                    _doubleEditControl = new DoubleValueEditControl[3];

                    // (scale) lock icon?
                    if (((CompLayerPropertyPoint3D)prop).HasValueLock)
                    {
                        Image valueLockImage = new Image();
                        valueLockImage.Width = 10;
                        valueLockImage.Height = 10;
                        valueLockImage.Stretch = Stretch.None;
                        valueLockImage.Margin = new Thickness(0, -1, 3, 0);
                        valueLockImage.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
                        valueLockImage.MouseLeftButtonDown += new MouseButtonEventHandler(ValueLockImage_MouseLeftButtonDown);

                        Binding myBinding = new Binding("ValueLock");
                        myBinding.Mode = BindingMode.TwoWay;
                        myBinding.Source = (CompLayerPropertyPoint3D)prop;
                        myBinding.Converter = new ValueLockImageConverter();
                        valueLockImage.SetBinding(Image.SourceProperty, myBinding);

                        PropertyValuesPanel.Children.Add(valueLockImage);
                    }

                    // X
                    _doubleEditControl[0] = createEditControlPoint3D("CurrentXValue");
                    PropertyValuesPanel.Children.Add(_doubleEditControl[0]);

                    if (appendChar.Length > 0)
                        PropertyValuesPanel.Children.Add(createAppendCharLabel(appendChar));

                    PropertyValuesPanel.Children.Add(createCommaLabel());

                    // Y
                    _doubleEditControl[1] = createEditControlPoint3D("CurrentYValue");
                    PropertyValuesPanel.Children.Add(_doubleEditControl[1]);

                    if (appendChar.Length > 0)
                        PropertyValuesPanel.Children.Add(createAppendCharLabel(appendChar));

                    PropertyValuesPanel.Children.Add(createCommaLabel());

                    // Z
                    _doubleEditControl[2] = createEditControlPoint3D("CurrentZValue");
                    PropertyValuesPanel.Children.Add(_doubleEditControl[2]);

                    if (appendChar.Length > 0)
                        PropertyValuesPanel.Children.Add(createAppendCharLabel(appendChar));

                    break;

                case LayerPropertyValueTypes.Double:
                    _doubleEditControl = new DoubleValueEditControl[1];
                    if (prop.PropertyValueMeaning == LayerPropertyValueMeaning.Timestamp)
                        _doubleEditControl[0] = createEditControlDouble("CurrentValue", ValueEditControlDisplayTypes.Timestamp);
                    else
                        _doubleEditControl[0] = createEditControlDouble("CurrentValue", ValueEditControlDisplayTypes.Plain);

                    if (prop.PropertyValueMeaning == LayerPropertyValueMeaning.Timestamp)
                        _doubleEditControl[0].ValuePrecision = 0.001;

                    PropertyValuesPanel.Children.Add(_doubleEditControl[0]);

                    if (appendChar.Length > 0)
                        PropertyValuesPanel.Children.Add(createAppendCharLabel(appendChar));

                    break;

                case LayerPropertyValueTypes.String:
                    _stringEditControl = new StringValueEditControl[1];
                    _stringEditControl[0] = createEditControlString("CurrentValue");
                    PropertyValuesPanel.Children.Add(_stringEditControl[0]);
                    break;

                case LayerPropertyValueTypes.RDIdent:
                    _replayPlayerEditControl = new RDIdentValueEditControl[1];
                    _replayPlayerEditControl[0] = createEditControlRDIdent("CurrentValue");
                    PropertyValuesPanel.Children.Add(_replayPlayerEditControl[0]);
                    break;
            }
        }

        private DoubleValueEditControl createEditControlDouble(string bindName, ValueEditControlDisplayTypes displayType = ValueEditControlDisplayTypes.Plain)
        {
            Binding myBinding;
            CompLayerPropertyBase prop = (CompLayerPropertyBase)DataContext; 

            DoubleValueEditControl dec = new DoubleValueEditControl();
            dec.IsEditable = prop.IsEditable;
            dec.DataContext = DataContext;
            dec.DisplayType = displayType;
            dec.ValueName = bindName;
            dec.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;

            myBinding = new Binding(bindName);
            myBinding.Mode = BindingMode.TwoWay;
            myBinding.Source = prop;
            dec.SetBinding(DoubleValueEditControl.ValueProperty, myBinding);

            myBinding = new Binding("HasBoundProperty");
            myBinding.Mode = BindingMode.OneWay;
            myBinding.Source = prop;
            dec.SetBinding(DoubleValueEditControl.HasBoundPropertyProperty, myBinding);

            dec.PropertyChanged += handleValueChange;
            dec.DoubleValueChanged += new System.EventHandler<DoubleValueChangedEventArgs>(dec_DoubleValueChanged);
            dec.writeLabelValue();

            return dec;
        }

        private StringValueEditControl createEditControlString(string bindName)
        {
            Binding myBinding;

            StringValueEditControl sec = new StringValueEditControl();
            sec.DataContext = DataContext;
            sec.ValueName = bindName;
            sec.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;

            myBinding = new Binding(bindName);
            myBinding.Mode = BindingMode.TwoWay;
            myBinding.Source = (CompLayerPropertyString)DataContext;
            sec.SetBinding(StringValueEditControl.ValueProperty, myBinding);
            sec.PropertyChanged += handleValueChange;

            sec.writeLabelValue();

            return sec;
        }

        private RDIdentValueEditControl createEditControlRDIdent(string bindName)
        {
            Binding myBinding;

            RDIdentValueEditControl rpec = new RDIdentValueEditControl();
            rpec.DataContext = DataContext;
            rpec.ValueName = bindName;
            rpec.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;

            myBinding = new Binding(bindName);
            myBinding.Mode = BindingMode.TwoWay;
            myBinding.Source = (CompLayerPropertyRDIdent)DataContext;
            rpec.SetBinding(RDIdentValueEditControl.ValueProperty, myBinding);

            rpec.PropertyChanged += handleValueChange;
            rpec.RDIdentValueChanged += new System.EventHandler<RDIdentValueChangedEventArgs>(rpec_RDIdentValueChanged);

            rpec.writeLabelValue();

            return rpec;
        }

        void rpec_RDIdentValueChanged(object sender, RDIdentValueChangedEventArgs e)
        {
            Debug.WriteLine("handling value changed");
            CompLayerPropertyRDIdent prop = (CompLayerPropertyRDIdent)DataContext;
            CustomRDKeyFrame kf = (CustomRDKeyFrame)prop.GetKeyAtTime(LfsRecordController.FrameIndex);

            int keyFrameIndex = -1;
            if (kf != null)
                keyFrameIndex = kf.KeyFrameIndex;

            LfsRecordController.RecordUserAction(new UserActionKeyFrameRDIdentChanged()
                {
                    LayerIndex = prop.ParentLayer.LayerIndex,
                    PropertyIndex = prop.PropertyIndex,
                    KeyFrameIndex = keyFrameIndex,
                    OldValue = e.OldValue,
                    NewValue = e.NewValue
                });
            LfsRecordController.CreateUndoLevel();
        }

        private DoubleValueEditControl createEditControlPoint3D(string bindName)
        {
            CompLayerPropertyBase prop = (CompLayerPropertyBase)DataContext; 
            Binding myBinding;

            DoubleValueEditControl dec = new DoubleValueEditControl();
            dec.IsEditable = prop.IsEditable;
            dec.DataContext = prop;
            dec.ValueName = bindName;
            dec.ValueScaleFactor = (prop.PropertyName == "Scale") ? 100 : 1;
            dec.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;

            myBinding = new Binding(bindName);
            myBinding.Mode = BindingMode.TwoWay;
            myBinding.Source = (CompLayerPropertyPoint3D)DataContext;
            dec.SetBinding(DoubleValueEditControl.ValueProperty, myBinding);

            myBinding = new Binding("HasBoundProperty");
            myBinding.Mode = BindingMode.OneWay;
            myBinding.Source = (CompLayerPropertyPoint3D)DataContext;
            dec.SetBinding(DoubleValueEditControl.HasBoundPropertyProperty, myBinding);

            dec.PropertyChanged += handleValueChange;
            dec.DoubleValueChanged += new System.EventHandler<DoubleValueChangedEventArgs>(dec_DoubleValueChanged);

            dec.writeLabelValue();

            return dec;
        }

        void dec_DoubleValueChanged(object sender, DoubleValueChangedEventArgs e)
        {
            DoubleValueEditControl dec = (DoubleValueEditControl)sender;
            CompLayerPropertyBase prop = (CompLayerPropertyBase)DataContext;

            Vector3D vecOffset;

            // Figure out which value has changed so we can store the appropriate user action
            if (prop.PropertyValueType == LayerPropertyValueTypes.Point3D)
            {
                CustomPoint3DKeyFrame kf = (CustomPoint3DKeyFrame)prop.GetKeyAtTime(LfsRecordController.FrameIndex);
                if (kf != null)
                {
                    if (dec.ValueName == "CurrentXValue")
                    {
                        vecOffset = new Vector3D(e.NewValue - e.OldValue, 0, 0);
                    }
                    else if (dec.ValueName == "CurrentYValue")
                    {
                        vecOffset = new Vector3D(0, e.NewValue - e.OldValue, 0);
                    }
                    else
                    {
                        vecOffset = new Vector3D(0, 0, e.NewValue - e.OldValue);
                    }
                }
                else
                {
                    if (dec.ValueName == "CurrentXValue")
                    {
                        vecOffset = new Vector3D(e.NewValue - e.OldValue, 0, 0);
                    }
                    else if (dec.ValueName == "CurrentYValue")
                    {
                        vecOffset = new Vector3D(0, e.NewValue - e.OldValue, 0);
                    }
                    else
                    {
                        vecOffset = new Vector3D(0, 0, e.NewValue - e.OldValue);
                    }
                }

                SubmitUserAction(vecOffset);
            }
            else if (prop.PropertyValueType == LayerPropertyValueTypes.Double)
            {
                SubmitUserAction(e.NewValue - e.OldValue);
            }
        }

        private void SubmitUserAction(double offset)
        {
            CompLayerPropertyBase prop = (CompLayerPropertyBase)DataContext;

            int keyFrameIndex = -1;
            CustomKeyFrame kf = prop.GetKeyAtTime(LfsRecordController.FrameIndex);
            if (kf != null)
            {
                keyFrameIndex = kf.KeyFrameIndex;
            }

            LfsRecordController.RecordUserAction(new UserActionKeyFrameDoubleValueChanged()
            {
                LayerIndex = prop.ParentLayer.LayerIndex,
                PropertyIndex = prop.PropertyIndex,
                KeyFrameIndex = keyFrameIndex,
                ValueOffset = offset
            });
            LfsRecordController.CreateUndoLevel();
        }

        private void SubmitUserAction(Vector3D offset)
        {
            CompLayerPropertyBase prop = (CompLayerPropertyBase)DataContext;

            int keyFrameIndex = -1;
            CustomKeyFrame kf = prop.GetKeyAtTime(LfsRecordController.FrameIndex);
            if (kf != null)
            {
                keyFrameIndex = kf.KeyFrameIndex;
            }

            LfsRecordController.RecordUserAction(new UserActionKeyFramePoint3DValueChanged()
            {
                LayerIndex = prop.ParentLayer.LayerIndex,
                PropertyIndex = prop.PropertyIndex,
                KeyFrameIndex = keyFrameIndex,
                ValueOffset = offset
            });
            LfsRecordController.CreateUndoLevel();
        }

        private void handleValueChange(object sender, PropertyChangedEventArgs e)
        {
            Debug.WriteLine("Handling keyframe change");
            CompLayerPropertyBase prop = (CompLayerPropertyBase)DataContext;
            CustomKeyFrame kf = prop.OnCurrentValueChange();
            if (kf != null)
            {
                // Record user action of added keyframe
                LfsRecordController.RecordUserAction(new UserActionAddKeyframe()
                {
                    LayerIndex = kf.ParentLayer.LayerIndex,
                    PropertyIndex = kf.ParentProperty.PropertyIndex,
                    KeyFrame = kf.DataStructure()
                });
            }
            prop.ParentLayer.SetCurrentTransformMatrix();
            prop.ParentLayer.UpdatedCurrentValues = CompLayerPropertyUpdateTypes.Position | CompLayerPropertyUpdateTypes.Rotation | CompLayerPropertyUpdateTypes.Scale;
            prop.ParentLayer.DoneUpdatingCurrentValues = !prop.ParentLayer.DoneUpdatingCurrentValues;

            ServicesFactory.EventService.GetEvent<CompLayerPropertyChangedEvent>().Publish(
                new CompLayerPropertyChangedEventArgs() { Sender = this, PropertyType = e.PropertyName , UpdateCurrentValues = false });
        }

        private static Label createCommaLabel()
        {
            Label commaLabel = new Label();
            commaLabel.Content = " , ";
            commaLabel.Padding = new Thickness(0, 2, 0, 0);
            commaLabel.FontSize = 10;
            commaLabel.Foreground = new SolidColorBrush(Color.FromArgb(255, 237, 170, 5));
            commaLabel.IsHitTestVisible = false;
            return commaLabel;
        }

        private static Label createAppendCharLabel(string appendChar)
        {
            Label appendLabel = new Label();
            appendLabel.Content = appendChar;
            appendLabel.Padding = new Thickness(0, 2, 0, 0);
            appendLabel.FontSize = 10;
            appendLabel.Foreground = new SolidColorBrush(Color.FromArgb(255, 237, 170, 5));
            appendLabel.IsHitTestVisible = false;
            return appendLabel;
        }

        private void Label_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            e.Handled = true;
            if (!Keyboard.IsKeyDown(Key.LeftShift))
            {
                LfsRecordController.project.DeselectAll();
            }

            CompLayerPropertyBase prop = (CompLayerPropertyBase)DataContext;
            prop.ParentLayer.IsSelected = true;
            prop.IsSelected = true;
            prop.selectAllKeyFrames();
        }

        private void IsAnimatedImage_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            e.Handled = true;
            CompLayerPropertyBase prop = (CompLayerPropertyBase)DataContext;

            // Animation icon does not function if property has a binding
            if (!prop.IsEditable || prop.HasBoundProperty)
                return;

            if (prop.IsAnimated)
            {
                // Store keyframe delete action before actually deleting them by disaling IsAnimated
                foreach (CustomKeyFrame kf in prop.KeyFrames)
                {
                    LfsRecordController.RecordUserAction(new UserActionDeleteKeyFrame()
                    {
                        LayerIndex = prop.ParentLayer.LayerIndex,
                        PropertyIndex = prop.PropertyIndex,
                        KeyFrame = kf.DataStructure()
                    });
                }

                prop.IsAnimated = false;

                // Record user action
                LfsRecordController.RecordUserAction(new UserActionPropertyIsAnimatedChanged()
                    {
                        LayerIndex = prop.ParentLayer.LayerIndex,
                        PropertyIndex = prop.PropertyIndex,
                        IsAnimated = prop.IsAnimated
                    });
            }
            else
            {
                prop.IsAnimated = true;

                // Record user action
                LfsRecordController.RecordUserAction(new UserActionPropertyIsAnimatedChanged()
                {
                    LayerIndex = prop.ParentLayer.LayerIndex,
                    PropertyIndex = prop.PropertyIndex,
                    IsAnimated = prop.IsAnimated
                });

                // Record the action for the newly created key
                CustomKeyFrame kf = prop.KeyFrames[0];
                LfsRecordController.RecordUserAction(new UserActionAddKeyframe()
                {
                    LayerIndex = kf.ParentLayer.LayerIndex,
                    PropertyIndex = kf.ParentProperty.PropertyIndex,
                    KeyFrame = kf.DataStructure()
                });
            }

            LfsRecordController.CreateUndoLevel();
        }

        private void KeyWalkerLeftImage_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            e.Handled = true;
            CompLayerPropertyBase prop = (CompLayerPropertyBase)DataContext;
            //Debug.WriteLine("KW has prevframe : " + prop.HasPreFrames + " - next prev framenum : " + prop.getFrameNumOfPreviousKey());
            if (prop.HasPreFrames)
            {
                int newFrameIndex = prop.FrameNumOfPreviousKey();
                ServicesFactory.EventService.GetEvent<FrameIndexChangedEvent>().Publish(new FrameIndexChangedEventArgs() { Sender = this, NewFrameIndex = newFrameIndex, UpdateCurrentValues = true, UpdateReplayPosition = true });
            }
        }

        private void KeyWalkerDotImage_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            e.Handled = true;
            CompLayerPropertyBase prop = (CompLayerPropertyBase)DataContext;
            if (!prop.IsEditable || prop.HasBoundProperty || prop.HasCurrentFrame)
                return;

            LfsRecordController.project.DeselectAll();

            CustomKeyFrame kf = prop.SetKeyAtCurrentTime(LfsRecordController.FrameIndex);
            kf.IsSelected = true;

            LfsRecordController.RecordUserAction(new UserActionAddKeyframe()
            {
                LayerIndex = kf.ParentLayer.LayerIndex,
                PropertyIndex = kf.ParentProperty.PropertyIndex,
                KeyFrame = kf.DataStructure()
            });

            // Check if there is a previous keyframe (its properties probably changed)

            
            // Check if there is a next keyframe (its properties probably changed)


            LfsRecordController.CreateUndoLevel();
        }

        private void KeyWalkerRightImage_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            e.Handled = true;
            CompLayerPropertyBase prop = (CompLayerPropertyBase)DataContext;
            if (prop.HasPostFrames)
            {
                int newFrameIndex = prop.FrameNumOfNextKey();
                ServicesFactory.EventService.GetEvent<FrameIndexChangedEvent>().Publish(new FrameIndexChangedEventArgs() { Sender = this, NewFrameIndex = newFrameIndex, UpdateCurrentValues = true, UpdateReplayPosition = true });
            }
        }

        private void ValueLockImage_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            e.Handled = true;
            // We can assume we have a point3d one here, because only those have a value lock
            CompLayerPropertyPoint3D prop = (CompLayerPropertyPoint3D)DataContext;
            prop.ValueLock = !prop.ValueLock;
        }
    }
}
